home *** CD-ROM | disk | FTP | other *** search
- /******************************************************************************
- Smaller Installer © 1996 Bill Goodman, All Rights Reserved
- *******************************************************************************
-
- Locate Folder Hook Example
-
- This installer hook procedure allows the user to use a dialog to select the
- location for an installed folder.
-
- To build this hook procedure, compile this code and create a code resource
- (Type:SICR, ID:501, non-preloaded, nonpurgeable, unlocked, unprotected,
- non-sysheap). Add this resource to the "LocateFolderHook.rsrc" file. Copy all the
- resources in "LocateFolderHook.rsrc" to your installer's resource file.
-
- Set the strings in the STR#:500 resource to the following values:
- 1 - Name of folder to relocate (must be unique in archive)
- 2 - Prompt string for install dialog
- 3 - Prompt string for remove dialog
-
- *******************************************************************************
- Notes
- o There appears to be a bug in System 7.1 which occurs when the "Trash"
- item is selected in the desktop folder. This selection may cause the
- folder selection dialog to display "Trash" or the name of the current
- volume.
- o If the user's system has more than 6 folders and volumes to display on
- the desktop, the only way to select the desktop folder is to click on the
- grayed out trash item. It is not obvious that this will select the
- desktop so many users may have trouble figuring this out. Furthermore,
- for older systems, the bug described above may prevent selecting the
- desktop folder using this method. As a result, you should expect that the
- user may NOT be able to select the desktop folder using this hook code.
- ******************************************************************************/
-
- // This file is compatible with version 2.1 of the universal headers
- #include <Aliases.h>
- #include <Folders.h>
- #include <LowMem.h>
- #include <StandardFile.h>
- #include <TextUtils.h>
-
- #ifdef __MWERKS__
- #include <A4Stuff.h>
- #endif
-
- #include <SetUpA4.h>
-
- #include "SIHookProc.h"
-
-
- /******************************************************************************
- Module Internal Function Prototypes
- ******************************************************************************/
- void BeforeItemFunction(void);
- void BeginOperationFunction(void);
-
- Boolean GetFolder(StringPtr prompt, short *foldVRefNum, long *foldDirID);
- pascal short GetFolderDlgHookProc(short item, DialogPtr theDialog);
- pascal Boolean GetFolderDlgFilterProc(DialogPtr theDialog, EventRecord *theEvent, short *itemHit);
- pascal Boolean AllFileFilterProc(CInfoPBPtr infoPtr);
- short TruncateLengthStr(StringPtr theStr, short maxWidth);
- Boolean CreateNewFolder(void);
-
-
- /******************************************************************************
- Constant Definitions
- ******************************************************************************/
- // Dialog Definitions
- #define getFolderDlg 500 // GetFolder dialog (modified SFGetFile)
- #define oldOpen_getFolderDlg 1 // OLD_OPEN button
- #define cancel_getFolderDlg 3 // CANCEL button
- #define newOpen_getFolderDlg 11 // NEW_OPEN button
- #define select_getFolderDlg 12 // SELECT button
- #define newFolder_getFolderDlg 13 // NEW_FOLDER button
-
- #define newFolderDlg 501 // NewFolder dialog
- #define ok_newFolderDlg 1 // OK button
- #define name_newFolderDlg 3 // Name edit text field
-
- // Alert Definitions
- #define changeVolAlrt 510 // "If you wish to use a different volume, please use the DRIVE button to select the volume and try again."
- #define folderErrAlrt 511 // "The new folder could not be created."
-
- // Indexed String Definitions
- #define genIStrID 500 // Indexed string resource ID
- enum
- {
- folderNameIStr = 1, // Name of folder to relocate
- installPromptIStr, // Prompt string for install dialog
- removePromptIStr // Prompt string for remove dialog
- };
-
- #define getFolderIStrID 501 // Indexed string resource ID
- enum
- {
- selectBeginIStr = 1, // Title for SELECT button "Select “"
- ellipsisIStr, // Ellipsis string "…"
- selectEndIStr, // End of title for SELECT button "”"
- newFolderTitleIStr, // Default name of new folder "untitled folder"
- desktopIStr // Alternate name for desktop folder "Desktop"
- };
-
-
- /******************************************************************************
- Module Variable Definitions
- ******************************************************************************/
- SIHookParmBlk *gParms; // Global pointer to parameter block
- unsigned char gEmptyStr[] = "\p"; // Global empty string
- long gFolderDirID; // Directory ID of destination folder
-
- // GetFolder variables
- Boolean gFolderSelected; // True if user selected a folder
- short gLastSFSaveDisk; // Value of SFSaveDisk when selection last changed
- SFReply gSFReply; // Standard file reply record
-
-
- /*****************************************************************************/
- pascal void main(
- SIHookParmBlk *parmBlk // Pointer to parameter block
- )
- /******************************************************************************
- This is the main entry point for the installer hook procedure.
- ******************************************************************************/
- {
- #ifdef __MWERKS__
- long holdA4;
- #endif
-
- // Set up access to global variables
- #ifdef THINK_C
- RememberA0();
- SetUpA4();
- #endif
-
- #ifdef __MWERKS__
- holdA4 = SetCurrentA4();
- RememberA4();
- #endif
-
- gParms = parmBlk;
-
- switch (gParms->function)
- {
- case siHookBeginOperation:
- BeginOperationFunction();
- break;
-
- case siHookBeforeItem:
- BeforeItemFunction();
- break;
- }
-
- // Restore original A4 value
- #ifdef THINK_C
- RestoreA4();
- #endif
-
- #ifdef __MWERKS__
- SetA4(holdA4);
- #endif
- }
-
-
- /*****************************************************************************/
- void BeginOperationFunction(void)
- /******************************************************************************
- Input parameters:
- "targetVRefNum" Volume reference number of target volume
- "groupAPFlags" Groups currently selected
- "groupQUSel"
- "groupVZSel"
- "group32Flags"
- "group64Flags"
- "group96Flags"
- "groupEnvironFlags"
- "passwordPtr" Pointer to password string
- "filesRemaining" Number of files remaining to install or remove
- "bytesRemaining" Number of bytes of data remaining to install or remove
- "doingRemove" Non-zero if doing remove operation
-
- Returns:
- "passwordPtr" Pointer to password string
- "result" Hook result code (siHookNoErr, siHookQuit, siHookAbort)
-
- This function is called when the install button or the remove button is
- clicked to begin installing or removing files.
- ******************************************************************************/
- {
- Str255 promptStr;
- short vRefNum;
-
- // Display dialog prompting user to select the location of the folder
- LMSetSFSaveDisk(-gParms->targetVRefNum); // Set default directory to root
- LMSetCurDirStore(fsRtDirID);
- GetIndString(promptStr, genIStrID, gParms->doingRemove ? removePromptIStr : installPromptIStr);
- if (promptStr[0] == 0)
- goto FatalError;
- if (GetFolder(promptStr, &vRefNum, &gFolderDirID))
- goto Abort; // User cancelled or a serious error occurred
- if (vRefNum != gParms->targetVRefNum)
- { // Error - user specified a location off the target volume
- CautionAlert(changeVolAlrt, NULL);
- goto Abort;
- }
- return; // Good completion
-
- // Abort operation
- Abort:
- gParms->result = siHookAbort;
- return;
-
- // Fatal error occurred
- FatalError:
- SysBeep(1);
- gParms->result = siHookQuit; // Force installer to quit
- }
-
-
- /*****************************************************************************/
- void BeforeItemFunction(void)
- /******************************************************************************
- Input parameters:
- "targetVRefNum" Volume reference number of target volume
- "groupAPFlags" Groups currently selected
- "groupQUSel"
- "groupVZSel"
- "group32Flags"
- "group64Flags"
- "group96Flags"
- "groupEnvironFlags"
- "filesRemaining" Number of files remaining to install or remove
- "bytesRemaining" Number of bytes of data remaining to install or remove
- "doingRemove" Non-zero if doing remove operation
- "anyItemsSkipped" Non-zero if any item has been skipped during operation
- "desVRefNum" Volume reference number of destination volume
- "desDirID" Directory ID of destination directory
- "itemName" Name of item to install or remove
- "itemIsFolder" Non-zero if item is a folder
- "fileType" File type (files only)
- "fileCreator" File creator (files only)
- "createDate" Creation date (files only)
- "lastModDate" Last modification date (files only)
- "rsrcForkLen" Length of resource fork (files only)
- "dataForkLen" Length of data fork (files only)
-
- Returns:
- "result" Hook result code (siHookNoErr, siHookQuit, siHookAbort, siHookSkip, siHookItemDone)
- "desDirID" Directory ID of destination directory
- "itemName" Name of item to install or remove
- "itemInfo" Reference passed to AfterItem call
-
- This function is called before each item is installed or removed.
- ******************************************************************************/
- {
- static Str63 folderName = "\p";
- CInfoPBRec ib;
-
- if (gParms->itemIsFolder)
- { // Check to see if this item is the folder to relocate
- if (folderName[0] == 0)
- { // Get folder name from resource
- GetIndString(folderName, genIStrID, folderNameIStr);
- if (folderName[0] == 0)
- goto FatalError;
- }
-
- if (!EqualString(gParms->itemName, folderName, true, true))
- return; // Names do not match - no action required
-
- // Names match - change the destination directory
- ib.dirInfo.ioVRefNum = gParms->targetVRefNum;
- ib.dirInfo.ioDrDirID = gFolderDirID;
- ib.dirInfo.ioNamePtr = gParms->itemName;
- ib.dirInfo.ioFVersNum = 0;
- ib.dirInfo.ioFDirIndex = -1; // Use DirID without name
- if (PBGetCatInfoSync(&ib))
- goto FatalError;
- gParms->desDirID = ib.dirInfo.ioDrParID;
- }
- return;
-
- // Fatal error occurred
- FatalError:
- SysBeep(1);
- gParms->result = siHookQuit; // Force installer to quit
- }
-
-
- /*****************************************************************************/
- Boolean GetFolder(
- StringPtr prompt, // Prompt string
- short *foldVRefNum, // Volume reference number of selected folder
- long *foldDirID // Directory ID of selected folder
- )
- /******************************************************************************
- Prompt the user to select a folder. Return TRUE if user cancels operation or
- an error occurs. Otherwise, return FALSE and return the selected folder in
- "foldVRefNum" and "foldDirID".
- ******************************************************************************/
- {
- Point sfWhere = { -1, -1 };
- FSSpec aliasFSSpec;
- Boolean targetIsFolder;
- Boolean wasAliased;
- CInfoPBRec ib;
-
- if (gParms->groupEnvironFlags & siHookEnvSystem6OrLower)
- { // System 6 does not support auto-centering of dialog - set dialog location
- sfWhere.h = 56;
- sfWhere.v = 69;
- }
-
- gFolderSelected = false;
- gLastSFSaveDisk = 0; // Force first update
- ParamText(prompt, gEmptyStr, gEmptyStr, gEmptyStr);
- SFPGetFile(sfWhere, gEmptyStr, AllFileFilterProc, -1, NULL, GetFolderDlgHookProc,
- &gSFReply, getFolderDlg, GetFolderDlgFilterProc);
- if (!gFolderSelected)
- return true; // User cancelled or an error occurred
-
- if (gSFReply.fName[0])
- { // Filename in list selected - must be a folder alias or
- // volume alias since we filtered out all normal files
- // Resolve alias file
- if (FSMakeFSSpec(-LMGetSFSaveDisk(), LMGetCurDirStore(), gSFReply.fName, &aliasFSSpec))
- return true; // Error - could not create FSSpec for alias file
- if (ResolveAliasFile(&aliasFSSpec, true, &targetIsFolder, &wasAliased))
- return true; // Error - could not resolve alias
- if (!targetIsFolder || !wasAliased)
- return true; // Error - invalid alias (shouldn't happen)
-
- ib.dirInfo.ioVRefNum = aliasFSSpec.vRefNum;
- ib.dirInfo.ioDrDirID = aliasFSSpec.parID;
- ib.dirInfo.ioNamePtr = aliasFSSpec.name;
- ib.dirInfo.ioFVersNum = 0;
- ib.dirInfo.ioFDirIndex = 0; // Use DirID and name
- if (PBGetCatInfoSync(&ib))
- return true; // Error
- *foldDirID = ib.dirInfo.ioDrDirID;
- *foldVRefNum = aliasFSSpec.vRefNum;
- }
- else
- {
- *foldVRefNum = -LMGetSFSaveDisk();
- if (gSFReply.fType != 0)
- *foldDirID = gSFReply.fType; // Folder in list selected
- else
- *foldDirID = LMGetCurDirStore(); // Nothing selected - use CurDirStore
- }
- return false;
- }
-
-
- /*****************************************************************************/
- pascal Boolean GetFolderDlgFilterProc(
- register DialogPtr theDialog,
- EventRecord *theEvent,
- short *itemHit
- )
- /******************************************************************************
- This filter proc is used with the GetFolder dialog. Enable/disable the new
- OPEN button to match the state of the original OPEN button (a copy of the
- button is used to eliminate the automatic bold outline).
- ******************************************************************************/
- {
- long holdA4;
- ControlHandle newOpenHdl;
- ControlHandle oldOpenHdl;
- ControlHandle selectHdl;
- Rect itemRect;
- short itemType;
- short length;
- short maxNameWidth;
- Str255 name;
- Str255 selectTitle;
- Str255 ellipsisStr;
- Str255 selectEndStr;
- short deskVRefNum;
- long deskDirID;
- CInfoPBRec ib;
- static long lastCurDirStore;
- static long lastFType;
- static Str255 lastName;
-
- // Set up to access globals
- #ifdef THINK_C
- SetUpA4();
- #endif
- #ifdef __MWERKS__
- holdA4 = SetUpA4();
- #endif
-
- // Set enabled state of NEW_OPEN button to be same as OLD_OPEN
- GetDialogItem(theDialog, newOpen_getFolderDlg, &itemType, (Handle *) &newOpenHdl, &itemRect);
- GetDialogItem(theDialog, oldOpen_getFolderDlg, &itemType, (Handle *) &oldOpenHdl, &itemRect);
- if ((*newOpenHdl)->contrlHilite != (*oldOpenHdl)->contrlHilite)
- HiliteControl(newOpenHdl, (*oldOpenHdl)->contrlHilite);
-
- if ((gLastSFSaveDisk != LMGetSFSaveDisk()) || (lastCurDirStore != LMGetCurDirStore())
- || (lastFType != gSFReply.fType) || !EqualString(lastName, gSFReply.fName, true, true))
- { // Selection has changed - update SELECT button title
- if (gSFReply.fName[0])
- { // Filename in list selected - must be a folder alias or
- // volume alias since we filtered out all normal files
- BlockMove(gSFReply.fName, &name, gSFReply.fName[0] + 1);
- }
- else
- {
- if (gSFReply.fType == 0)
- { // Nothing selected - use CurDirStore
- ib.dirInfo.ioDrDirID = LMGetCurDirStore();
- if (gParms->groupEnvironFlags & siHookEnvSystem7OrHigher)
- { // Check for special case of desktop folder selected
- if (!FindFolder(-LMGetSFSaveDisk(), kDesktopFolderType, kDontCreateFolder, &deskVRefNum, &deskDirID))
- if (deskDirID == LMGetCurDirStore())
- { // Desktop folder selected - use text string for title
- GetIndString(name, getFolderIStrID, desktopIStr); // Get "Desktop"
- if (name[0] != 0)
- goto Name_Done;
- }
- }
- }
- else
- { // Folder in list selected
- ib.dirInfo.ioDrDirID = gSFReply.fType;
- }
- ib.dirInfo.ioVRefNum = -LMGetSFSaveDisk();
- ib.dirInfo.ioNamePtr = name;
- ib.dirInfo.ioFVersNum = 0;
- ib.dirInfo.ioFDirIndex = -1; // Use DirID without name
- if (PBGetCatInfoSync(&ib))
- name[0] = 0; // Clear name if error occurs
- }
- Name_Done:
-
- // Set new title of SELECT button
- GetIndString(selectTitle, getFolderIStrID, selectBeginIStr); // Get "Select “"
- GetIndString(selectEndStr, getFolderIStrID, selectEndIStr); // Get "”"
-
- GetDialogItem(theDialog, select_getFolderDlg, &itemType, (Handle *) &selectHdl, &itemRect);
- maxNameWidth = itemRect.right - itemRect.left - StringWidth(selectTitle) - StringWidth(selectEndStr) - 20;
- length = TruncateLengthStr(name, maxNameWidth); // Truncate name if necessary
-
- BlockMove(&name[1], &selectTitle[selectTitle[0] + 1], length);
- selectTitle[0] += length;
- if (length != name[0])
- { // Name was truncated - add truncation character(s)
- GetIndString(ellipsisStr, getFolderIStrID, ellipsisIStr); // Get "…" (may be multiple bytes)
- BlockMove(&ellipsisStr[1], &selectTitle[selectTitle[0] + 1], ellipsisStr[0]);
- selectTitle[0] += ellipsisStr[0];
- }
- BlockMove(&selectEndStr[1], &selectTitle[selectTitle[0] + 1], selectEndStr[0]);
- selectTitle[0] += selectEndStr[0];
- SetControlTitle(selectHdl, selectTitle);
-
- // Save current folder parameters for later comparison
- gLastSFSaveDisk = LMGetSFSaveDisk();
- lastCurDirStore = LMGetCurDirStore();
- lastFType = gSFReply.fType;
- BlockMove(gSFReply.fName, &lastName, gSFReply.fName[0] + 1);
- }
- #ifdef THINK_C
- RestoreA4();
- #endif
- #ifdef __MWERKS__
- RestoreA4(holdA4);
- #endif
- return false;
- }
-
-
- /*****************************************************************************/
- pascal short GetFolderDlgHookProc(
- short item,
- DialogPtr theDialog
- )
- /******************************************************************************
- Process the SELECT and NEW_OPEN buttons in the Extract dialog.
- ******************************************************************************/
- {
- long holdA4;
- ControlHandle oldOpenHdl;
- Rect itemRect;
- short itemType;
- EventRecord anEvent;
-
- // Set up to access globals
- #ifdef THINK_C
- SetUpA4();
- #endif
- #ifdef __MWERKS__
- holdA4 = SetUpA4();
- #endif
-
- switch (item)
- {
- case select_getFolderDlg:
- // SELECT button clicked - translate to CANCEL to terminate
- gFolderSelected = true;
- item = cancel_getFolderDlg;
- break;
-
- case newOpen_getFolderDlg:
- // NEW_OPEN button clicked - translate to OLD_OPEN
- GetDialogItem(theDialog, oldOpen_getFolderDlg, &itemType, (Handle *) &oldOpenHdl, &itemRect);
- if ((*oldOpenHdl)->contrlHilite == 0) // Ensure OLD_OPEN is enabled first
- { // Folder or volume
- if (gSFReply.fName[0] == 0)
- item = sfHookOpenFolder; // Folder or volume
- else
- { // File, file alias or folder/volume alias
- // Assume it is a folder/volume alias since no files are displayed
- OSEventAvail(0, &anEvent);
- if (anEvent.modifiers & optionKey)
- item = sfHookGoToAliasTarget; // Go to alias target if OPTION key
- else
- item = sfHookOpenAlias; // Folder/volume alias
- }
- }
- break;
-
- case newFolder_getFolderDlg: // NEW_FOLDER button - Create new folder
- if (!CreateNewFolder())
- item = sfHookRebuildList;
- break;
- }
- #ifdef THINK_C
- RestoreA4();
- #endif
- #ifdef __MWERKS__
- RestoreA4(holdA4);
- #endif
- return item;
- }
-
-
- /*****************************************************************************/
- pascal Boolean AllFileFilterProc(
- CInfoPBPtr infoPtr // Pointer to file info block
- )
- /******************************************************************************
- This procedure filters out all files.
- ******************************************************************************/
- {
- return true;
- }
-
-
- /*****************************************************************************/
- short TruncateLengthStr(
- StringPtr theStr, // String to truncate
- short maxWidth // Maximum width of string
- )
- /******************************************************************************
- Return the maximum number of characters which may be printed in the
- specified width. Note that the graph port must be set with the appropriate
- font info.
- ******************************************************************************/
- {
- short posList[256];
- short index;
- Str255 ellipsisStr;
-
- MeasureText(theStr[0], &theStr[1], posList);
- index = theStr[0];
- if (posList[index] > maxWidth)
- { // Truncation required
- GetIndString(ellipsisStr, getFolderIStrID, ellipsisIStr);
- maxWidth -= StringWidth(ellipsisStr); // Allow space for "…"
- while (posList[--index] > maxWidth)
- ;
- }
- return index;
- }
-
-
- /*****************************************************************************/
- Boolean CreateNewFolder(void)
- /******************************************************************************
- Display a dialog and allow the user to select a name for a new folder.
- Create the folder in the directory specified by -SFSaveDisk and CurDirStore
- and change CurDirStore to the new directory. Return FALSE if folder is
- created successfully. Return TRUE if an error occurs or the user cancels the
- operation.
- ******************************************************************************/
- {
- Str255 folderName;
- DialogPtr dlgPtr;
- short itemType;
- Handle itemHdl;
- Rect itemRect;
- short index;
- short item;
- Boolean nameIsBlank;
- long newDirID;
-
- GetIndString(folderName, getFolderIStrID, newFolderTitleIStr); // Get "untitled folder"
- dlgPtr = GetNewDialog(newFolderDlg, NULL, (WindowPtr) -1);
- if (!dlgPtr)
- return true;
- GetDialogItem(dlgPtr, name_newFolderDlg, &itemType, &itemHdl, &itemRect);
- SetDialogItemText(itemHdl, folderName);
- SelectDialogItemText(dlgPtr, name_newFolderDlg, 0, 32767); // Select folder name
- ShowWindow(dlgPtr);
- ModalDialog(NULL, &item);
- GetDialogItemText(itemHdl, folderName);
- DisposeDialog(dlgPtr);
- if (item != ok_newFolderDlg)
- return true; // Cancelled by user
- if (folderName[0] > 31)
- folderName[0] = 31; // Truncate if name is too long
-
- // Replace all ':' characters with '-' and ensure there is at least one non-space
- // character in the name
- nameIsBlank = true;
- for (index = folderName[0]; index != 0; index--)
- {
- if ((folderName[index] == ':') && (CharByte((Ptr) &folderName[1], index - 1) == 0))
- folderName[index] = '-';
- if (folderName[index] != ' ')
- nameIsBlank = false;
- }
- if (nameIsBlank)
- goto Error;
-
- if (DirCreate(-LMGetSFSaveDisk(), LMGetCurDirStore(), folderName, &newDirID))
- goto Error;
-
- LMSetCurDirStore(newDirID); // Move to new directory
- return false; // Good completion
-
- Error: // Error occurred while creating directory
- CautionAlert(folderErrAlrt, NULL);
- return true;
- }
-